//////////////////////////////////////////////////
// EASIM project
// easim.net.Node
// 2007/6/6 12:56
// Cong Liu
// gzcong@gmail.com

// Copyright (C) 2007
// All rights reserved.
// Refer to LICENSE of JIST (Java In Simulation Time) for terms and conditions of use.

package easim.net;

import jist.swans.field.Placement;
import jist.swans.field.FieldInterface;
import jist.swans.radio.RadioNoise;
import jist.swans.radio.RadioNoiseIndep;
import jist.swans.radio.RadioNoiseAdditive;
import jist.swans.radio.RadioInfo;
import jist.swans.mac.Mac802_11;
import jist.swans.mac.MacAddress;
import jist.swans.mac.MacInterface;
import jist.swans.net.NetAddress;
import jist.swans.net.NetInterface;
import jist.swans.route.RouteInterface;
import jist.swans.misc.Location;
import jist.swans.misc.Message;
import jist.swans.Constants;

import jist.runtime.JistAPI;
import jist.runtime.JistAPI.DoNotRewrite;

import java.util.LinkedList;

public abstract class Node implements NodeInterface, DoNotRewrite
{

  protected int id;
  protected float x;
  protected float y;

  protected Net net;
  protected RadioNoise radio;
  protected boolean onField;

  public Node(int id) {
    this.id = id;
    onField = false;

    // radio
    radio = new RadioNoiseIndep(id, Field.radioInfo);
    //RadioNoise radio = new RadioNoiseAdditive(i, radioInfo);

    // mac
    Mac802_11 mac = new Mac802_11(new MacAddress(id), radio.getRadioInfo());

    // net
    net = new Net(this);

    // node entity hookup
    radio.setMacEntity(mac.getProxy());
    net.setInterface(mac.getProxy());
    mac.setRadioEntity(radio.getProxy());
    mac.setNetEntity(net.getProxy(), (byte)1);
  }

  public int getId() {
    return id;
  }

  public float getX() {
    return x;
  }

  public float getY() {
    return y;
  }

  public boolean isOnField() {
    return onField;
  }

  public void send(Message msg, int dstId) {
    net.send(msg, dstId);
  }

  /////////////////////
  // Net
  //

  public static class Net implements NetInterface
  {

    protected NetInterface self;
    protected Node host;

    protected MacInterface mac;
    protected boolean macBusy;
    protected LinkedList messageQueue;

    public Net(Node node) {
      self = (NetInterface)JistAPI.proxy(this, NetInterface.class);
      host = node;
    }

    public NetInterface getProxy()
    {
      return self;
    }

    public void setInterface(MacInterface macEntity)
    {
      mac = macEntity;
      macBusy = false;
      messageQueue = new LinkedList();
    }

    protected static class NetMessage implements Message
    {

      protected int srcId;
      protected int dstId;
      protected Message msg;

      public NetMessage(Message msg, int srcId, int dstId) {
        this.msg = msg;
        this.srcId = srcId;
        this.dstId = dstId;
      }

      public int getSrc() {
        return srcId;
      }

      public int getDst() {
        return dstId;
      }

      public Message getPayload() {
        return msg;
      }

      public int getSize() {
        return msg.getSize();
      }

      public void getBytes(byte[] msg, int offset) {
        throw new RuntimeException("not implemented.");
      }

    } // end class NetMessage

    public void send(Message msg, int dst) {
      NetMessage netMsg = new NetMessage(msg, host.id, dst);
      messageQueue.addLast(netMsg);
      if(! macBusy) pump(0);
    }

    ////////////////////////
    // NetInterface
    //

    public NetAddress getAddress() throws JistAPI.Continuation {
      throw new RuntimeException("not implemented.");
    }

    public void receive(Message msg, MacAddress lastHop, byte macId, boolean promiscuous) {
      NetMessage netMsg = (NetMessage)msg;
      JistAPI.sleep(Constants.NET_DELAY);
      host.receive(netMsg.getPayload(), netMsg.getSrc(), netMsg.getDst());
    }

    public void send(Message msg, NetAddress dst, short protocol, byte priority, byte ttl) {
      throw new RuntimeException("not implemented.");
    }

    public void send(jist.swans.net.NetMessage.Ip msg, int interfaceId, MacAddress nextHop) {
      throw new RuntimeException("not implemented.");
    }

    public void pump(int netid) {
      if(messageQueue.size() == 0 || (! host.onField)) {
        macBusy = false;
      }
      else {
        macBusy = true;
        NetMessage msg = (NetMessage)messageQueue.removeFirst();
        JistAPI.sleep(Constants.NET_DELAY);
        mac.send(msg, new MacAddress(msg.getDst()));
      }
      host.pump(messageQueue.size(), macBusy);
    }

  }

}
